import java.io.BufferedReader
import java.lang.Exception
import java.lang.NumberFormatException
import java.lang.StringBuilder
import java.util.*
import javax.print.attribute.IntegerSyntax

/**
 * 主函数:
 * fun function(方法,函数),关键字,标记一个方法
 * main 方法名,主方法和Java一样都是以main命名,是一个关键字,所以标记为黄色
 * args 参数名,是一个形参
 * Array<String> String 类型的数组
 * 注意: 与Java不同,kotlin中声明变量时,名字在前,类型在后,中间用:(冒号)表示
 * 返回值先不讲,食欲与Java类似,也是有没有返回值的类型,只是可以省略
 * 与Java不同,每句代码的结尾不需要分号,只要换行就代表这一句代码结束
 */
fun main() {
//    println("Hello,world!")
//    a = false
//    list.add("kotlin")
//    println(list)
//    println(str)
//    println("Hello$name")
//    println("1 + 2 = ${1 + 2}")
//    println("a ${if (a >= 10) "大于等于10" else "小于等于10"}")
//    val person = Person("lc", 12)
//    val personJava = PersonJava("lc", 12)
//
//    personJava.age = 13
//    println(personJava)
//    println(person)
//
//    val rectangle = Rectangle(10, 12)
//    println("是否是个正方形:${rectangle.isSquare}")
//    val rectangleJava = RectangleJava(10, 10)
//    println("Java是否是个正方形:${rectangleJava.isSquare}")
//    println(mix(Color.RED, Color.YELLOW))
//    println(mix(Color.YELLOW, Color.RED))
//    play()
//    binary()
//    println(regex('1'))
//    println(list2.javaClass)
//    println(set.javaClass)
//    println(map.javaClass)
//    println(mutableList.javaClass)
//    println(list2.max())
//    println(set.last())
//    println(list2)
//    println(list2)
//    println(joinToString(list2, "|", "(", ")"))
//    println(joinToString(separator = "||", prefix = "++", collection = list2))
//    println(joinToString(list2, separator = "|||"))
    println("kotlin".lastChar())
    println(list2.joinToString())
    println("kotlin".lastChar)
}

/**
 * 方法(函数):
 * 有返回值,没有返回值的方法,在kotlin中,其实所有的方法都有返回值,只是没有返回值的方法他的返回值
 * 是个Unit,相当于Java中的void
 * a:Int a 是参数名 , :Int是类型 : 有继承实现,或者它是个什么东西的意思
 * 整个方法后的:Int,代表当前方法的返回值是一个Int类型
 * 在kotlin中基本类型比如int,boolean,都是大写开头,都是包装类型,有对应的方法可以使用
 */
//fun max(a: Int, b: Int): Int {
//    return if (a > b) a else b
//}

/**
 * kotlin特性,表达式方法体(函数体)
 * 如果一个方法的方法体是由单个表达式构成,可以使用这个表达式作为完整的方法体,并且去掉大括号和return
 * 同时方法的返回值:Int,也可以省略
 */
fun max(a: Int, b: Int) = if (a > b) a else b


/**
 * 变量:
 * val (value) 不可变引用,在初始化之后不能再次赋值,对应Java中final修饰符
 * var (variable) 可变引用,这种变量的值可以被改变,对应Java中的普通变量
 * :String 可以省略,也就是val和var可以自动识别类型,初始化是什么类型,之后使用或者赋值时就是什么类型
 * 初始化是一个int,再次赋值时也必须是一个int,不能改变类型,可以改变值
 * 有一个情况比较特殊,使用val声明list的时候,他的集合中的值是可以被改变的
 * const 恒定的,使用const val声明的变量相当于Java中声明一个常量
 */
const val str: String = "这是一个String"

var a = 123

val list = arrayListOf("Java")

/**
 * 字符串模板(拼接字符串)
 * 使用$符号可以直接在字符串中引用某些变量
 * 什么止咳加{}大括号来进行计算和判断
 * 注意在写句式的时候,先写完整的字符串,再加入${},这样不会乱
 */
val name = "LC"

enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0),
    ORANGE(255, 165, 0),
    YELLOW(255, 255, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255);

    fun rgb() = (r * 256 + g) * 256 + b
}

//fun getColorString(color: Color): String {
//    when (color) {
//        Color.YELLOW -> return "黄色"
//        Color.BLUE -> return "蓝色"
//        Color.RED -> return "红色"
//        Color.GREEN -> return "绿色"
//        Color.ORANGE -> return "橙色"
//    }
//}

//第一次省略,可以return提前,让when直接返回字符串
//fun getColorString(color: Color): String {
//    return when (color) {
//        Color.YELLOW -> "黄色"
//        Color.BLUE -> "蓝色"
//        Color.RED -> "红色"
//        Color.GREEN -> "绿色"
//        Color.ORANGE -> "橙色"
//    }
//}

//第二次省略,通过表达式,直接省略return
fun getColorString(color: Color) = when (color) {
//    Color.YELLOW -> "黄色"
//    Color.BLUE -> "蓝色"
//    Color.RED -> "红色"
//    Color.GREEN -> "绿色"
//    Color.ORANGE -> "橙色"
    Color.RED, Color.YELLOW, Color.ORANGE -> "暖色调"
    Color.GREEN -> "中性色"
    Color.BLUE -> "冷色调"
}

/**
 * 方法的赋值,when和if如果有返回值的情况,可以直接变量赋值
 */
//val b = 2
//var max = when {
//    a > b -> {
//        println(a)
//        a
//    }
//    else -> b
//}
//
//var max1 = if (a > b) {
//    println(a)
//    a
//} else b

var max3 = max2(1, 2)

fun max2(a: Int, b: Int): Int {
    return if (a > b) {
        println(a)
        a
    } else {
        b
    }
}

//使用when,判断集合中的元素
fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(Color.RED, Color.YELLOW) -> "橙色"
    setOf(Color.YELLOW, Color.BLUE) -> "绿色"
    else -> "不知道是什么颜色"
}

//在when中,可以不写条件,直接判断分支结构
fun mix2(c1: Color, c2: Color) = when {
    (c1 == Color.RED && c2 == Color.YELLOW || c1 == Color.YELLOW && c2 == Color.RED) -> "橙色"
    (c1 == Color.YELLOW && c2 == Color.BLUE || c1 == Color.BLUE && c2 == Color.YELLOW) -> "率色"
    else -> "不知道什么颜色"
}

/**
 * 智能转换
 * is 相当于Java中的instanceOf
 */
val animal = Animal(12)
fun anim() {
    if (animal is Dog) {
        animal.dig()
    }
}

/**
 * 强转 as 相当于Java中的 ((Dog)animal).dig()
 */
val animal2 = Animal(12)
//val dog = animal2 as Dog

/**
 * 区间 1..10 1到10并且包含10  1 until 10 1到10 但是不包含10
 */

val oneToTen = 1..10
val oneToTen2 = 1 until 10

fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -> "FizzBuzz"
    i % 5 == 0 -> "Buzz"
    i % 3 == 0 -> "Fizz"
    else -> "$i"
}

/**
 * i 是个形参,写什么都可以,按照for循环的习惯,一般都写作i
 * in 在某个区间之内,或者list或者数组或者区间
 *
 * 循环:
 * while和do-while循环和在Java中没有任何区别,可以直接使用
 */

fun play() {
    for (i in 1..100) {
        println(fizzBuzz(i))
    }
}

fun binary() {
    val binaryReps = TreeMap<Char, String>()//声明了一个map,kv结构使用TreeMap是为了让键排序
    for (c in 'A'..'F') {
        val binary = Integer.toBinaryString(c.toInt()) //把A到F的ASCII码转换为二进制
        binaryReps[c] = binary //根据键c 把值存入map中
//        binaryReps.put(c,binary)
    }

    //迭代map时,可以在循环中添加2个参数,第一个代表key,第二个代表value
    for ((key, value) in binaryReps) {
        println("$key = $value")
    }

    //.withIndex() 相当于给了每个元素多了个index的key,element就是它的值
    val list = arrayListOf(10, 11, 1001)
    for ((index, element) in list.withIndex()) {
        println("$index:$element")
    }
}

//验证一个char是否是一个字母
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNumber(c: Char) = c in '0'..'9'

fun regex(c: Char) = when {
    isLetter(c) -> "这是个字母"
    isNumber(c) -> "这是个数字"
    else -> "可能是个中文,也可能是个符号"
}

/**
 * kotlin中的异常
 * 与Java类似
 * 只有抛出异常不太一样,一个异常可以做为值赋值给一个变量
 */
val c = if (a > 0) a else throw Exception("这是个负数或者0")

fun readNumber(reader: BufferedReader): Int? = try {
    //捕获下方代码块中的异常,如果有异常,则走catch,没有就把try走完
    val line = reader.readLine()
    Integer.parseInt(line)
} catch (e: NumberFormatException) {
    //如果发生异常后,会走此代码块
    null
} finally {
    //甭管发不发生异常,都会走finally
    reader.close()
}

/**
 * String? 代表这个声明的对象可以为空
 * 在kotlin中 ? 很重要,代表一个类型是否可以为空,如果声明一个类型可以为空的话,必须要加?
 * 在kotlin中String 和String? 是2个不同的概念,空和非空是2种不同的类型
 * 非空对象可以给可空对象赋值,也就是c1 可以给c2赋值,相反则不可以
 * !! 代表非空断言,表示这个值一定不为空
 */
var c1: String = "123" //非空对象
var c2: String? = "123"//可空对象
val c3: String? = null

fun c22() {
//    c1 = c2 //一个可空对象无法给一个非空对象赋值
    c2 = c1 //非空对象可以给可空对象赋值
    c1 = c2!! //非空断言,代表一定不为空,人为的操作
}

/**
 * 集合
 * 在kotlin中直接listOf就是new出一个集合
 * 常用的其实是mutableListOf,创建可变集合
 * to 并不是一个特殊的结构,它是一个方法,类似于Java中的: 因为kotlin中:被占用,所以用to来构建键值对
 */
val mutableList = mutableListOf(1, 2, 3)//可变的list
val list2 = listOf(1, 2, 3)
val set = setOf(1, 2, 3)
val map = mapOf(1 to "one", 2 to "two")

fun <T> Collection<T>.joinToString(
//    collection: Collection<T>,//集合的顶层接口,代表这个参数可以是个list或者set
    separator: String = "||",//分隔符
    prefix: String = "{",//开始的符号
    postfix: String = "}"//结束的符号
): String {
    val result = StringBuilder(prefix) //首先创建一个可拼接的字符串,默认添加开始的符号
    for ((index, element) in this.withIndex()) {
        if (index > 0) {
            result.append(separator) //大于0的情况下在拼接字符串中添加一个分隔符
        }
        result.append(element)//每次循环在分隔符之后添加一个集合中的元素
    }
    result.append(postfix)//循环完毕,在String的末尾添加结束符号
    return result.toString()//最终返回出这个String
}

/**
 *  扩展方法(函数)
 *  给一个已有的类或者类型添加一个方法,在Java中是不可能实现的,String无法继承
 *  通常Java中都是通过方法的参数形式来修改一final修饰的类,继承重写的方式只能实现普通的类
 */
fun String.lastChar() = this[this.length - 1]

/**
 * 扩展属性
 * 与方法不同,属性相当于扩展了API,可以用来访问属性,使用的是属性的语法,而不是方法的语法,没有括号
 * 被称为属性,但是可以没有任何状态,因为没有地方存储,也就是属性不会占用存储空间
 */
val String.lastChar: Char
    get() = get(length - 1)
